package u16888

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"lottery/conf"
	"lottery/constant"
	"lottery/models"
	"lottery/utils"

	"code.google.com/p/go.net/context"
	"github.com/PuerkitoBio/goquery"
	"github.com/astaxie/beego/logs"
	"github.com/robertkrimen/otto"
)

type U16888 struct {
	hostName      string // 登录到的子域
	loginName     string
	lastLoginName string
	ua            string
	playCount     int64

	gameChan     chan []*models.Game
	gameDataChan chan []*models.GameData

	wg        *sync.WaitGroup
	stoped    chan struct{} // 销毁
	agentInfo *models.Agent
	jar       *cookiejar.Jar
	client    *http.Client
}

func (u *U16888) Platform() int {
	return constant.U16888
}

func (u *U16888) errActionResp(err error) (resp *models.ActionResp) {
	resp = new(models.ActionResp)
	if err != nil {
		resp.IsSuccess = false
		resp.Msg = err.Error()
		return
	}

	resp.IsSuccess = true
	return
}

func (u *U16888) AgentInfo() *models.Agent {
	return u.agentInfo
}

func (u *U16888) IsStop() bool {
	select {
	case <-u.stoped:
		return true
	default:
		return false
	}
}

func (u *U16888) Login() (resp *models.ActionResp) {
	u.switchIP(u.getIP())

	html, cookies, err := utils.GetCookieWithHtml("http://www.u16888.com/zh-cn/betting.aspx")
	if err != nil {
		return u.errActionResp(err)
	}

	domain, _ := url.Parse("http://www.u16888.com")
	if domain != nil {
		// set cookie
		u.jar.SetCookies(domain, cookies)
	}

	buf := bytes.NewBuffer(html)
	doc, err := goquery.NewDocumentFromReader(buf)
	if err != nil {
		return u.errActionResp(err)
	}

	var js string
	doc.Find("head script").Each(func(index int, sel *goquery.Selection) {
		if strings.Contains(sel.Text(), "BackgroundImageCache") {
			js = sel.Text()
		}
	})

	// tk 参数获取
	var tk string
	reg, _ := regexp.Compile("tilib_Token\\([^\\)\\s]*\\)")
	strs := reg.FindAllString(js, 2)
	for _, str := range strs {
		if strings.Contains(str, "oev") {
			reg, _ := regexp.Compile("\\[[\\d]{1}[^\\]\\s]*\\]")
			tk = reg.FindString(str)
		}
	}
	// 清洗
	tk = strings.Replace(tk, "[", "", -1)
	tk = strings.Replace(tk, "]", "", -1)
	tk = strings.Replace(tk, "'", "", -1)

	// tag
	tag, _ := doc.Find("#tag").Attr("value")
	fingerprint, _ := doc.Find("#fingerprint").Attr("value")
	hidCK, _ := doc.Find("#HidCK").Attr("value")

	params := make(url.Values)
	params.Set("id", u.agentInfo.LoginName)
	params.Set("password", u.agentInfo.Passwd)
	params.Set("lang", "zh-cn")
	params.Set("tk", tk)
	params.Set("5", "1")
	params.Set("type", "form")
	params.Set("tzDiff", "0")
	params.Set("HidCK", hidCK)
	params.Set("tag", tag)
	params.Set("fingerprint", fingerprint)

	httpReq, _ := http.NewRequest("POST", "http://www.u16888.com/web/public/process-sign-in.aspx", strings.NewReader(params.Encode()))
	header := make(http.Header)
	header.Set("User-Agent", "okhttp/3.7.0")
	header.Set("Content-Type", "application/x-www-form-urlencoded")
	httpReq.Header = header

	httpResp, err := u.client.Do(httpReq)
	if err != nil {
		return u.errActionResp(err)
	}

	if err := u.setAgentInfo(httpResp); err != nil {
		return &models.ActionResp{IsSuccess: false, Msg: err.Error()}
	}

	if u.lastLoginName == "" {
		u.lastLoginName = u.loginName
	} else if u.lastLoginName != u.loginName {
		u.lastLoginName = u.loginName
	}
	return &models.ActionResp{IsSuccess: true}
}

func (u *U16888) setAgentInfo(res *http.Response) (err error) {
	doc, err := goquery.NewDocumentFromResponse(res)
	if err != nil {
		return
	}

	balanceStr := doc.Find("#bet-credit").Text()
	balanceStr = strings.Replace(balanceStr, ",", "", -1)
	balance, _ := strconv.ParseFloat(balanceStr, 64)
	u.agentInfo.Balance = int(balance * 100)
	logs.Debug("%+v", u.agentInfo)
	u.loginName = res.Request.URL.Query().Get("loginname")
	u.hostName = res.Request.URL.Host
	logs.Debug("%s", u.loginName)
	if u.loginName == "" {
		err = errors.New("登录失败")
	}
	return
}

func (u *U16888) ReLogin() (reps *models.ActionResp) {
	// u.agentInfo.TickReset()
	return u.Login()
}

// 下单
// team 1 主场  2 客场
func (u *U16888) Place(price int, team int, tickData interface{}, gameData *models.GameData, switchIp bool) (resp *models.ActionResp) {
	acceptIfAny := 0
	// if !isFirst {
	// 	acceptIfAny = 1
	// }
	resp = new(models.ActionResp)
	orderUrl := fmt.Sprintf("http://%s/web-root/restricted/ticket/confirm.aspx?loginname=%s&sameticket=0&betcount=%d&stake=%d&ostyle=1&stakeInAuto=25&betpage=14&acceptIfAny=%d&autoProcess=0&autoRefresh=1&oid=%s&js=1",
		u.hostName, u.loginName, u.playCount, price/100, acceptIfAny, gameData.HandicapId)

	logs.Notice("利记下单 %s", orderUrl)

	orderReq, _ := http.NewRequest("GET", orderUrl, nil)
	orderReq.Header.Set("User-Agent", u.ua)

	orderResp, err := u.client.Do(orderReq)
	if err != nil {
		return u.errActionResp(err)
	}
	defer orderResp.Body.Close()

	data, err := ioutil.ReadAll(orderResp.Body)
	if err != nil {
		return u.errActionResp(err)
	}

	if strings.Contains(string(data), "等待中") {
		resp.IsSuccess = true
	} else {
		resp.IsSuccess = false
		resp.ExtData = false
		if strings.Contains(string(data), "showOrderResult") {
			logs.Notice("利记需重试下单")
			var tmp int
			if _, err := fmt.Sscanf(strings.TrimSpace(string(data)), "$M('ticket').showOrderResult(%d,%d);", &tmp, &u.playCount); err == nil {
				logs.Notice("利记确认重试下单 %d", u.playCount)
				resp.ExtData = true
			}
		}

		if strings.Contains(string(data), "您的投注金额必须高于最低投注额") {
			u.playCount++
		}
		resp.Msg = string(data)
	}
	return
}

var tickJs = `
data = %s;
rate = data[9];
lowPrice = data[12];
highPrice = data[13];
count = data[19];
`

func (u *U16888) Tick(team int, gameData *models.GameData) (rate float64, tickData interface{}, discard bool, err error) {
	// u.agentInfo.TickInc()
	op := "h"
	rate = gameData.HRate
	if team == 2 {
		op = "a"
		rate = gameData.ARate
	}

	tickData = new(struct{})

	odds := fmt.Sprintf("%0.2f", rate)
	tickReqUrl := fmt.Sprintf("http://%s/web-root/restricted/ticket/ticket.aspx?loginname=%s&id=%s&op=%s&odds=%s&hdpType=1&isor=0&isLive=0&betpage=18&style=1",
		u.hostName, u.loginName, gameData.HandicapId, op, odds)

	tickReq, _ := http.NewRequest("GET", tickReqUrl, nil)
	refererUrl := fmt.Sprintf("http://%s/web-root/restricted/default.aspx?loginname=%s&redirect=true", u.hostName, u.loginName)
	tickReq.Header.Set("Referer", refererUrl)
	tickReq.Header.Set("User-Agent", u.ua)
	tickResp, err := u.client.Do(tickReq)
	if err != nil {
		return
	}

	defer tickResp.Body.Close()
	data, err := ioutil.ReadAll(tickResp.Body)
	if err != nil {
		return
	}

	// 换线
	if strings.Contains(string(data), "大小盘点数已变更") {
		discard = true
		return
	}

	leftIndex := bytes.IndexByte(data, '[')
	if leftIndex != -1 {
		rightIndex := bytes.LastIndexByte(data, ']')
		data = data[leftIndex : rightIndex+1]

		var (
			vm             *otto.Otto
			rateValue      otto.Value
			lowPriceValue  otto.Value
			highPriceValue otto.Value
			countValue     otto.Value
			lowPrice       int64
			highPrice      int64
		)

		vm, _, err = otto.Run(fmt.Sprintf(tickJs, string(data)))
		if err != nil {
			return
		}

		rateValue, err = vm.Get("rate")
		if err != nil {
			return
		}

		lowPriceValue, err = vm.Get("lowPrice")
		if err != nil {
			return
		}

		highPriceValue, err = vm.Get("highPrice")
		if err != nil {
			return
		}

		countValue, err = vm.Get("count")
		if err != nil {
			return
		}

		rateStr := rateValue.String()
		lowPrice, err = lowPriceValue.ToInteger()
		if err != nil {
			return
		}
		gameData.LowPrice = int(lowPrice) * 100 // 元转分

		highPrice, err = highPriceValue.ToInteger()
		if err != nil {
			return
		}
		gameData.HighPrice = int(highPrice) * 100 // 元转分

		u.playCount, err = countValue.ToInteger()
		if err != nil {
			return
		}

		// 点水成功
		if rateStr != fmt.Sprintf("%0.2f", rate) {
			rate, err = strconv.ParseFloat(rateStr, 64)
		}
	}
	return
}

func (u *U16888) CheckLogin() (resp *models.ActionResp) {
	resp = new(models.ActionResp)

	reqUrl := fmt.Sprintf("http://%s/web-root/restricted/top-module/action-data.aspx?action=bet-credit", u.hostName)
	httpReq, _ := http.NewRequest("GET", reqUrl, nil)
	httpReq.Header.Set("User-Agent", u.ua)
	httpResp, err := u.client.Do(httpReq)
	if err != nil {
		return u.errActionResp(err)
	}

	defer httpResp.Body.Close()
	data, err := ioutil.ReadAll(httpResp.Body)
	if err != nil {
		return u.errActionResp(err)
	}

	// 金额去掉逗号
	amountStr := strings.Replace(string(data), ",", "", -1)
	amountStr = utils.GetNumbStr(amountStr)
	if amountStr == "" {
		return u.errActionResp(errors.New("无法获取金额"))
	}

	amount, ok := utils.GetNumb(amountStr)
	if ok {
		u.agentInfo.Balance = int(amount * 100)
	}

	resp.IsSuccess = true
	return
}

func (u *U16888) Run() {
	u.wg.Add(1)
	defer u.wg.Done()

	// 时间间隔需要写入配置文件,作为渠道配置
	tick := time.NewTicker(time.Millisecond * time.Duration(conf.LoadResource().U16888Refresh))
	for {
		select {
		case _ = <-tick.C:
			games, gameDatas := u.getGameData()
			u.gameChan <- games
			u.gameDataChan <- gameDatas
		case _ = <-u.stoped:
			close(u.gameChan)
			close(u.gameDataChan)
			return
		}
	}
}

func (u *U16888) GetGames() (gamesChan <-chan []*models.Game) {
	return u.gameChan
}

func (u *U16888) GetGameData() <-chan []*models.GameData {
	return u.gameDataChan
}

func (u *U16888) switchIP(ip string) {
	if ip == "" {
		return
	}

	u.client.Transport = utils.GetTransport(ip, "")
}

func (u *U16888) getIP() string {
	if IPsRing == nil {
		return ""
	}

	return IPsRing.GetIp()
}

// TODO
func (u *U16888) GetOrderInfo(time.Time) (html string, err error) {

	//http://02hwx715m022.asia.u16888.com/web-root/restricted/betlist/running-bet-list.aspx?p=sb
	reqUrl := fmt.Sprintf("http://%s/web-root/restricted/betlist/running-bet-list.aspx?p=sb", u.hostName)
	httpReq, _ := http.NewRequest("GET", reqUrl, nil)
	logs.Notice("抓取利记订单信息 reqUrl:%s",reqUrl)
	//http://60c9w03k926a.asia.u16888.com/web-root/restricted/betlist/statement.aspx?popout=1
	////http://9v21z703830m.asia.u16888.com/web-root/restricted/default.aspx?loginname=1d7e4a6da5a1cdc6b447ff06cd1d247a&redirect=true
	httpReq.Header.Set("User-Agent", u.ua)
	//httpReq.Header.Set("Cookie", value)
	httpResp, err := u.client.Do(httpReq)
	if err != nil {
		return
	}
	defer httpResp.Body.Close()
	data, err := ioutil.ReadAll(httpResp.Body)
	if err != nil {
		return
	}
	html = string(data)
	logs.Notice("抓取利记订单成功！ len:%d err:%v", len(html), err)
	return

}

func (u *U16888) Destroy() {
	logs.Warn("u16888 destroy ...%d", u.agentInfo.Id)
	// 临时处理
	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*10)
	defer cancel()
	select {
	case <-ctx.Done():
		close(u.stoped)
	case <-u.stoped:
	}
	u.wg.Wait()
}
